#include "x11player.h"

bool yuv420ToRgb(char *yuv, int w, int h, char *rgb)
{
    unsigned char *y = new unsigned char[w*h];
    unsigned char *u = new unsigned char[w*h / 4];
    unsigned char *v = new unsigned char[w*h / 4];

    memcpy(y, yuv, w*h);
    memcpy(u, yuv + w * h, w*h / 4);
    memcpy(v, yuv + w * h * 5 / 4, w*h / 4);

    for (int i = 0; i < h; i++) {
        for (int j = 0; j < w; j++) {
            rgb[i*w*3 + 3*j] = 1.164*(y[i*w+j] - 16) + 1.596*(v[i / 4 * w + j / 2] - 128);//R
            rgb[i*w*3 + 3*j+1] = 1.164*(y[i*w + j] - 16) - 0.392*(u[i / 4*w+j/2] - 128) - 0.813*(v[i / 4 * w + j / 2] - 128);//G
            rgb[i*w*3 + 3*j+2] = 1.164*(y[i*w + j] - 16) + 2.017*(u[i / 4 * w + j / 2] - 128);  //B
        }
    }
    free(y);
    free(u);
    free(v);
    return true;
}

void StretchColors(void* pDest, int nDestWidth, int nDestHeight, int nDestBits, void* pSrc, int nSrcWidth, int nSrcHeight, int nSrcBits) {
    // 令dfAmplificationX和dfAmplificationY分别存储水平和垂直方向的放大率
    double dfAmplificationX = ((double)nDestWidth)/nSrcWidth;
    double dfAmplificationY = ((double)nDestHeight)/nSrcHeight;

    // 计算单个源位图颜色和目的位图颜色所占字节数
    const int nSrcColorLen = nSrcBits/8;
    const int nDestColorLen = nDestBits/8;

    // 进行图片缩放计算
    for(int i = 0; i<nDestHeight; i++) { // 处理第i行
        for(int j = 0; j<nDestWidth; j++) {   // 处理第i行中的j列
         //------------------------------------------------------
         // 以下代码将计算nLine和nRow的值,并把目的矩阵中的(i, j)点
         // 映射为源矩阵中的(nLine, nRow)点,其中,nLine的取值范围为
         // [0, nSrcHeight-1],nRow的取值范围为[0, nSrcWidth-1],

          double tmp = i/dfAmplificationY;
          int nLine = (int)tmp;

          if(tmp - nLine > 0.5)
              ++nLine;

          if(nLine >= nSrcHeight)
              --nLine;

          tmp = j/dfAmplificationX;
          int nRow = (int)tmp;

          if(tmp - nRow > 0.5)
          ++nRow;

          if(nRow >= nSrcWidth)
          --nRow;

          unsigned char *pSrcPos = (unsigned char*)pSrc + (nLine*nSrcWidth + nRow)*nSrcColorLen;
          unsigned char *pDestPos = (unsigned char*)pDest + (i*nDestWidth + j)*nDestColorLen;

         //把pSrcPos位置的前三字节拷贝到pDestPos区域
         *pDestPos++ = *pSrcPos++;
         *pDestPos++ = *pSrcPos++;
         *pDestPos++ = *pSrcPos++;

         if(nDestColorLen == 4)
          *pDestPos = 0;
        }
    }
}

X11Player::X11Player() {
    _disp = NULL;
    _image = NULL;
    _gc = NULL;
    _wId = 0;
    _visual = NULL;
    _srcDepth = 0;
    _image_bpp = 32;
    _drawWidth = 0;
    _drawHeight = 0;
    _imageData = NULL;
}

X11Player::~X11Player() {
    close();
}

int X11Player::init(unsigned int width, unsigned int height, unsigned long wId) {
    close();
    _width = width;
    _height = height;
    _wId = wId;
    _disp = XOpenDisplay(NULL);
    if (!_disp) {
        std::cout << "failed to open xdisplay" << std::endl;
        return 1;
    }

    _visual = DefaultVisual(_disp, DefaultScreen(_disp));
    _srcDepth = DefaultDepth(_disp, DefaultScreen(_disp));

    int dispWidth = DisplayWidth(_disp, DefaultScreen(_disp));
    int dispHeight = DisplayHeight(_disp, DefaultScreen(_disp));
    xRect r = getRect();
    _drawWidth = r.width;
    _drawHeight = r.height;
    // 创建一个当前窗口大小的XImage，这个XImage 在窗口大小发生改变时需要再次Create
    _image = XCreateImage(_disp, _visual, _srcDepth, ZPixmap, 0, NULL, r.width, r.height, 32, 0);
    if (_image) {
        _image_bpp = _image->bits_per_pixel;
        // 显示再大也不会超过屏幕直接申请一个屏幕大小的内存
        _imageData = new char[dispWidth * dispHeight * _image_bpp / 8];
        _image->data = _imageData;
    } else {
        std::cout << "failed to create ximage" << std::endl;
        return 2;
    }

    _gc = XCreateGC(_disp, _wId, 0, NULL);
    if (!_gc) {
        std::cout << "failed to create gc" << std::endl;
        return 3;
    }
    return 0;
}

void X11Player::close() {
    if (_disp) {
        XCloseDisplay(_disp);
        _disp = NULL;
    }
    if (_image) {
        delete _image;
        _image = NULL;
    }
    if (_gc) {
        delete _gc;
        // XFreeGC(_gc);
        _gc = NULL;
    }
    if (_imageData) {
        delete[] _imageData;
        _imageData = NULL;
    }
    _wId = 0;
}

xRect X11Player::getRect() {
    xRect r;
    if (_wId && _disp) {
        XWindowAttributes attr;
        XGetWindowAttributes(_disp, _wId, &attr);
        r.x = attr.x;
        r.y = attr.y;
        r.width = attr.width;
        r.height = attr.height;
    }
    return r;
}

void X11Player::autoFixedXImage(int width, int height) {
    if (_drawHeight != height || _drawWidth != width) {
        _drawWidth = width;
        _drawHeight = height;
        delete _image;  // 删掉上一个XImage，重新生成一个新的XImage
        _image = XCreateImage(_disp, _visual, _srcDepth, ZPixmap, 0, NULL, width, height, 32, 0);
        _image->data = _imageData;  // 记得重新指向
    }
}

int X11Player::inputRgb32(unsigned int width, unsigned int height, char *data) {
    // 先看一下绘画区域大小是否有改变
    xRect r = getRect();
    autoFixedXImage(r.width, r.height);
    // 把画面数据进行缩放，适配一下当前窗口的大小
    StretchColors(_imageData, r.width, r.height, 32, data, width, height, 32);
    XPutImage(_disp, _wId, _gc, _image, 0, 0, 0, 0, r.width, r.height);
    XSync(_disp, 1);
    XFlush(_disp);
    return 0;
}

int X11Player::inputYuv420p(unsigned int width, unsigned int height, char *data) {
    // 先看一下绘画区域大小是否有改变
    xRect r = getRect();
    autoFixedXImage(r.width, r.height);
    char* tmpBuf = new char[width * height * 4];
    if (!yuv420ToRgb(data, width, height, tmpBuf)) {
        std::cout << "failed yuv to rgb" << std::endl;
        return -1;
    }
    memcpy(_imageData, tmpBuf, width * height * 4);
    delete[] tmpBuf;
    StretchColors(_imageData, r.width, r.height, 32, data, width, height, 32);
    XPutImage(_disp, _wId, _gc, _image, 0, 0, 0, 0, r.width, r.height);
    XSync(_disp, 1);
    XFlush(_disp);
    return 0;
}
